package com.xbongbong.process.data;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasProcessDataEntity;
import com.xbongbong.paas.domain.entity.PaasProcessRelatedDataEntity;
import com.xbongbong.paas.domain.entity.PaasProcessRichTextEntity;
import com.xbongbong.paas.domain.entity.PaasProcessTaskEntity;
import com.xbongbong.paas.domain.entity.ext.FieldPermissionEntityExt;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.process.ProcessSummaryHelp;
import com.xbongbong.paas.model.PaasProcessDataModel;
import com.xbongbong.paas.model.PaasProcessNodeTaskModel;
import com.xbongbong.paas.model.PaasProcessRelatedDataModel;
import com.xbongbong.paas.model.PaasProcessTaskModel;
import com.xbongbong.paas.pojo.SummaryDataPoJo;
import com.xbongbong.paas.pojo.dto.ProcessDataAddDTO;
import com.xbongbong.paas.pojo.dto.ProcessDataUpdateDTO;
import com.xbongbong.paas.pojo.dto.SummaryDataDTO;
import com.xbongbong.paas.pojo.vo.ProcessDataAddVO;
import com.xbongbong.paas.pojo.vo.ProcessDataUpdateVO;
import com.xbongbong.paas.pojo.vo.SummaryDataVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.BaseDTO;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.PaasStageEnum;
import com.xbongbong.pro.enums.RichTextEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.formdata.pojo.SaasNeedRedundantAttrPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.enums.InstockTypeEnum;
import com.xbongbong.saas.enums.OutstockTypeEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractOutstockEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.PurchaseInstockEnum;
import com.xbongbong.saas.enums.business.RefundInstockEnum;
import com.xbongbong.saas.enums.business.WorkOrderOutstockEnum;
import com.xbongbong.saas.model.FundSetModel;
import com.xbongbong.saas.model.PaasProcessRichTextModel;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author: wufeng
 * @date: 2018/11/21 14:01
 * @desrcption:
 */
@Component
public class PaasProcessDataServiceImpl {

    private static final Logger LOG = LoggerFactory.getLogger(PaasProcessDataServiceImpl.class);

    @Resource
    private PaasProcessDataModel paasProcessDataModel;
    @Resource
    private PaasProcessTaskModel paasProcessTaskModel;
    @Resource
    private PaasProcessNodeTaskModel paasProcessNodeTaskModel;
    @Resource
    private PaasProcessRelatedDataModel paasProcessRelatedDataModel;
    @Resource
    private FundSetModel fundSetModel;
    @Resource
    private ProcessSummaryHelp processSummaryHelp;
    @Resource
    private PaasProcessRichTextModel paasProcessRichTextModel;

    public ProcessDataAddVO add(ProcessDataAddDTO processDataAddDTO) throws XbbException {
        ProcessDataAddVO processDataAddVO = new ProcessDataAddVO();
        try {
            PaasProcessDataEntity paasProcessDataEntity = new PaasProcessDataEntity();
            BeanUtil.copyProperties(processDataAddDTO, paasProcessDataEntity);
            paasProcessDataEntity.setCreatorId(processDataAddDTO.getCreatorId());
            paasProcessDataEntity.setData(JSONObject.toJSONString(processDataAddDTO.getData()));
            SaasNeedRedundantAttrPojo saasNeedRedundantAttrPojo = processDataAddDTO.getSaasNeedRedundantAttrPoJo();
            if (saasNeedRedundantAttrPojo != null) {
                setSaasNeedPojo(saasNeedRedundantAttrPojo);
                paasProcessDataEntity.setSaasNeedPoJo(JSON.toJSONString(processDataAddDTO.getSaasNeedRedundantAttrPoJo(), SerializerFeature.DisableCircularReferenceDetect));
            }
            //获取当前处于什么模式
            Long modelType = fundSetModel.getModelType(processDataAddDTO.getCorpid(), XbbRefTypeEnum.CRM);
            paasProcessDataEntity.setSysLong10(modelType);
            paasProcessDataModel.insert(paasProcessDataEntity);
            List<PaasProcessRelatedDataEntity> processRelatedDataList = new ArrayList<>();
            setRelatedData(paasProcessDataEntity, processRelatedDataList);
            if (!processRelatedDataList.isEmpty()) {
                paasProcessRelatedDataModel.insertBatch(processRelatedDataList);
            }
            processDataAddVO.setProcessDataId(paasProcessDataEntity.getId());
        } catch (Exception e) {
            LOG.error("PaasProcessData添加新的流程数据出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return processDataAddVO;
    }

    public ProcessDataUpdateVO update(ProcessDataUpdateDTO processDataUpdateDTO) throws XbbException {
        ProcessDataUpdateVO processDataUpdateVO = new ProcessDataUpdateVO();
        try {
            Object richTextData = null;
            //流程表单数据更新
            PaasProcessDataEntity paasProcessDataEntity = new PaasProcessDataEntity();
            BeanUtil.copyProperties(processDataUpdateDTO, paasProcessDataEntity);
            if (processDataUpdateDTO.getData() != null) {
                //移除富文本
                richTextData = processDataUpdateDTO.getData().remove(RichTextEnum.RICH_TEXT.getAttr());
                paasProcessDataEntity.setData(JSON.toJSONString(processDataUpdateDTO.getData(), SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteNullStringAsEmpty));
            }
            SaasNeedRedundantAttrPojo saasNeedRedundantAttrPojo = processDataUpdateDTO.getSaasNeedRedundantAttrPoJo();
            if (saasNeedRedundantAttrPojo != null) {
                try {
                    setSaasNeedPojo(saasNeedRedundantAttrPojo);
                    paasProcessDataEntity.setSaasNeedPoJo(JSON.toJSONString(processDataUpdateDTO.getSaasNeedRedundantAttrPoJo(), SerializerFeature.DisableCircularReferenceDetect));
                } catch (Exception e) {
                    // 19909 【成功部】点击列表页合同报错“表单不存在或者已删除”
                    // 循环引用，目前只有这样处理了
                    paasProcessDataEntity.setSaasNeedPoJo(null);
                }
            }
            paasProcessDataModel.update(paasProcessDataEntity);
            //流程富文本数据更新
            if (Objects.nonNull(richTextData)){
                PaasProcessRichTextEntity paasProcessRichTextEntity = new PaasProcessRichTextEntity();
                BeanUtil.copyProperties(processDataUpdateDTO, paasProcessRichTextEntity);
                paasProcessRichTextEntity.setData(richTextData.toString());
                paasProcessRichTextModel.updateByTaskId(paasProcessRichTextEntity);
            }
            List<PaasProcessRelatedDataEntity> processRelatedDataList = new ArrayList<>();
            // 拒绝，撤回，回退不修改数据，只需要修改审批状态
            Integer flowStatus = Objects.equals(processDataUpdateDTO.getFlowStatus(), FlowStatusEnum.REFUSE.getType()) ? FlowStatusEnum.IN_APPROVAL.getType() : processDataUpdateDTO.getFlowStatus();
            if (Objects.equals(flowStatus, FlowStatusEnum.REVOKE.getType()) || Objects.equals(flowStatus, FlowStatusEnum.REJECT.getType())) {
                paasProcessRelatedDataModel.updateFlowStatusByTaskId(processDataUpdateDTO.getTaskId(), processDataUpdateDTO.getCorpid(), flowStatus);
            } else {
                setRelatedData(paasProcessDataEntity, processRelatedDataList);
                if (!processRelatedDataList.isEmpty()) {
                    paasProcessRelatedDataModel.deleteByTaskId(processDataUpdateDTO.getTaskId(), processDataUpdateDTO.getCorpid());
                    paasProcessRelatedDataModel.insertBatch(processRelatedDataList);
                }
            }
            processDataUpdateVO.setProcessDataId(paasProcessDataEntity.getId());
            processDataUpdateVO.setProcessData(paasProcessDataEntity);
        } catch (Exception e) {
            LOG.error("PaasProcessData更新单条数据出错：", e);
            LOG.error("processDataUpdateDTO.getData()：" + processDataUpdateDTO.toString());
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return processDataUpdateVO;
    }

    public void deleteByFormDataId(Long formDataId, String corpid, Integer businessType) {
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", corpid);
        params.put("formDataId", formDataId);
        params.put("businessType", businessType);
        params.put(ParameterConstant.COLUMNS, "id,task_id,corpid");
        List<PaasProcessDataEntity> processDataList = paasProcessDataModel.list(params);
        if (processDataList == null || processDataList.size() == 0) {
            return;
        }
        // 删除数据记录
        PaasProcessDataEntity paasProcessDataEntity = processDataList.get(0);
        Long taskId = paasProcessDataEntity.getTaskId();
        paasProcessDataModel.delete(paasProcessDataEntity);
        // 删除主任务记录
        PaasProcessTaskEntity paasProcessTaskEntity = new PaasProcessTaskEntity();
        paasProcessTaskEntity.setId(taskId);
        paasProcessTaskEntity.setCorpid(corpid);
        paasProcessTaskModel.delete(paasProcessTaskEntity);
        // 删除节点任务数据
        paasProcessNodeTaskModel.deleteByTaskId(taskId, corpid);
    }

    public List<Long> getInApprovalDataIdList(List<Long> formDataIdList, String corpId, Integer saasMark, Integer businessType) {
        List<Long> dataIdList = new ArrayList<>();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", corpId);
        params.put("del", DelEnum.NORMAL.getDel());
        params.put("flowStatusIn", Arrays.asList(FlowStatusEnum.IN_APPROVAL.getType(), FlowStatusEnum.APPROVAL_PENDING.getType()));
        params.put("formDataIdIn", formDataIdList);
        if (SaasMarkEnum.SAAS.getCode().equals(saasMark)) {
            params.put("businessType", businessType);
        }
        params.put(ParameterConstant.COLUMNS, "form_data_id");
        List<PaasProcessDataEntity> processDataList = paasProcessDataModel.list(params);
        if (processDataList == null || processDataList.size() == 0) {
            return dataIdList;
        }
        processDataList.forEach(item -> dataIdList.add(item.getFormDataId()));
        return dataIdList;
    }

    public SummaryDataVO getSummaryAndTitle(SummaryDataDTO summaryDataDTO) throws XbbException {
        return processSummaryHelp.getSummaryAndTitle(summaryDataDTO);
    }



    public void setRelatedData(PaasProcessDataEntity paasProcessDataEntity, List<PaasProcessRelatedDataEntity> processRelatedDataList) {
        Integer businessType = paasProcessDataEntity.getBusinessType();
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        JSONObject data = JSON.parseObject(paasProcessDataEntity.getData());
        switch (xbbRefTypeEnum) {
            case CONTRACT:
                break;
            case PURCHASE:
                break;
            case WORK_ORDER:
                break;
            case CONTRACT_OUTSTOCK:
                JSONArray productArray = data.getJSONArray(ContractOutstockEnum.PRODUCT.getAttr());
                Long refId = data.getLong(ContractOutstockEnum.REF_ID.getAttr());
                if (!productArray.isEmpty()) {
                    getProductRelatedData(paasProcessDataEntity, processRelatedDataList, productArray, refId, XbbRefTypeEnum.CONTRACT.getCode());
                }
                break;
            case PURCHASE_INSTOCK:
                productArray = data.getJSONArray(PurchaseInstockEnum.PRODUCT.getAttr());
                refId = data.getLong(PurchaseInstockEnum.REF_ID.getAttr());
                if (!productArray.isEmpty()) {
                    getProductRelatedData(paasProcessDataEntity, processRelatedDataList, productArray, refId, XbbRefTypeEnum.PURCHASE.getCode());
                }
                break;
            case WORK_ORDER_OUTSTOCK:
                productArray = data.getJSONArray(WorkOrderOutstockEnum.PRODUCT.getAttr());
                refId = data.getLong(WorkOrderOutstockEnum.REF_ID.getAttr());
                if (!productArray.isEmpty()) {
                    getProductRelatedData(paasProcessDataEntity, processRelatedDataList, productArray, refId, XbbRefTypeEnum.WORK_ORDER.getCode());
                }
                break;
            case REFUND_INSTOCK:
                productArray = data.getJSONArray(RefundInstockEnum.PRODUCT.getAttr());
                refId = data.getLong(RefundInstockEnum.REF_ID.getAttr());
                if (!productArray.isEmpty()) {
                    getProductRelatedData(paasProcessDataEntity, processRelatedDataList, productArray, refId, XbbRefTypeEnum.REFUND.getCode());
                }
                break;
            case OTHER_INSTOCK:
                productArray = data.getJSONArray(InstockEnum.PRODUCT.getAttr());
                Integer type = data.getInteger(InstockEnum.TYPE.getAttr());
                refId = data.getLong(InstockEnum.REF_ID.getAttr());
                InstockTypeEnum instockTypeEnum = InstockTypeEnum.getByCode(type);
                if ((Objects.equals(InstockTypeEnum.TRANSFER_INSTOCK, instockTypeEnum) || Objects.equals(InstockTypeEnum.INVENTORY_INSTOCK, instockTypeEnum)) && !productArray.isEmpty()) {
                    getProductRelatedData(paasProcessDataEntity, processRelatedDataList, productArray, refId, instockTypeEnum.getBusinessType());
                }
                break;
            case OTHER_OUTSTOCK:
                productArray = data.getJSONArray(OutstockEnum.PRODUCT.getAttr());
                type = data.getInteger(OutstockEnum.TYPE.getAttr());
                refId = data.getLong(OutstockEnum.REF_ID.getAttr());
                OutstockTypeEnum outstockTypeEnum = OutstockTypeEnum.getByCode(type);
                if ((Objects.equals(OutstockTypeEnum.TRANSFER_OUTSTOCK, outstockTypeEnum) || Objects.equals(OutstockTypeEnum.INVENTORY_OUTSTOCK, outstockTypeEnum)) && !productArray.isEmpty()) {
                    getProductRelatedData(paasProcessDataEntity, processRelatedDataList, productArray, refId, outstockTypeEnum.getBusinessType());
                }
                break;
            default:
                break;
        }
    }

    private void getProductRelatedData(PaasProcessDataEntity paasProcessDataEntity, List<PaasProcessRelatedDataEntity> processRelatedDataList, JSONArray productArray, Long refId, Integer sourceBusinessType) {
        String corpid = paasProcessDataEntity.getCorpid();
        Integer businessType = paasProcessDataEntity.getBusinessType();
        Long taskId = paasProcessDataEntity.getTaskId();
        Integer flowStatus = Objects.equals(paasProcessDataEntity.getFlowStatus(), FlowStatusEnum.REFUSE.getType()) ? FlowStatusEnum.IN_APPROVAL.getType() : paasProcessDataEntity.getFlowStatus();
        Long formDataId = paasProcessDataEntity.getFormDataId();
        Long formId = paasProcessDataEntity.getFormId();
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject productObj = productArray.getJSONObject(i);
            Long productId = productObj.getLong(SelectProductEnum.PRODUCT.getAttr());
            Long refProductId = productObj.getLong(BusinessConstant.REF_PRODUCT_ID);
            Long productSubId = productObj.getLong(BusinessConstant.PRODUCT_SUB_ID);
            Double num = productObj.getDouble(SelectProductEnum.NUM.getAttr());
            PaasProcessRelatedDataEntity paasProcessRelatedDataEntity = new PaasProcessRelatedDataEntity();
            paasProcessRelatedDataEntity.setCorpid(corpid);
            paasProcessRelatedDataEntity.setFormId(formId);
            paasProcessRelatedDataEntity.setBusinessType(businessType);
            paasProcessRelatedDataEntity.setSaasMark(paasProcessDataEntity.getSaasMark());
            paasProcessRelatedDataEntity.setTaskId(taskId);
            paasProcessRelatedDataEntity.setFlowStatus(flowStatus);
            paasProcessRelatedDataEntity.setFormDataId(formDataId);
            paasProcessRelatedDataEntity.setFirstRefId(refId);
            paasProcessRelatedDataEntity.setSecondRefId(refProductId);
            paasProcessRelatedDataEntity.setThirdRefId(productSubId);
            paasProcessRelatedDataEntity.setFourthRefId(productId);
            paasProcessRelatedDataEntity.setValue(num);
            paasProcessRelatedDataEntity.setModuleType(1);
            paasProcessRelatedDataEntity.setSourceBusinessType(sourceBusinessType);
            processRelatedDataList.add(paasProcessRelatedDataEntity);
        }
    }

    /**
     * 处理摘要
     * @param removeAttrList 要移除的摘要字段list
     * @param summaryJsonArray 摘要
     * @param fieldPermissionEntityExt 字段权限实体
     * @author zcp
     * @since v1.0
     * @version v1.0
     * @update 2020/9/15 by zcp 要移除的可能是多个字段，改为list
     */
    private void handleSummaryDataPojo(List<String> removeAttrList, JSONArray summaryJsonArray, FieldPermissionEntityExt fieldPermissionEntityExt, JSONObject jsonObject, JSONObject dataJsonObject, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        if (CollectionsUtil.isNotEmpty(removeAttrList) && removeAttrList.contains(fieldPermissionEntityExt.getAttr())) {
            return;
        }
        // 检索摘要
        boolean summaryFlag = (fieldPermissionEntityExt.getSummary() != null && Objects.equals(fieldPermissionEntityExt.getSummary(), 1)) && (fieldPermissionEntityExt.getVisible() != null && Objects.equals(fieldPermissionEntityExt.getVisible(), 1));
        if (summaryFlag) {
            SummaryDataPoJo summaryDataPoJo = new SummaryDataPoJo();
            summaryDataPoJo.setFieldType(fieldPermissionEntityExt.getFieldType());
            summaryDataPoJo.setAttr(fieldPermissionEntityExt.getAttr());
            jsonObject.put(fieldPermissionEntityExt.getAttr(), dataJsonObject.get(fieldPermissionEntityExt.getAttr()));
            if (Objects.nonNull(explainMap.get(fieldPermissionEntityExt.getAttr())) &&
                    (Objects.equals(explainMap.get(fieldPermissionEntityExt.getAttr()).getFieldType(), FieldTypeEnum.LINK_BUSINESS_MULTI.getType()) || Objects.equals(explainMap.get(fieldPermissionEntityExt.getAttr()).getFieldType(), FieldTypeEnum.LINK_BUSINESS_SINGLE.getType()) || Objects.equals(explainMap.get(fieldPermissionEntityExt.getAttr()).getFieldType(), FieldTypeEnum.USER.getType()))) {
                jsonObject.put(explainMap.get(fieldPermissionEntityExt.getAttr()).getLinkedText(), dataJsonObject.get((explainMap.get(fieldPermissionEntityExt.getAttr()).getLinkedText())));
            }
            summaryJsonArray.add(summaryDataPoJo);
        } else {
            // 阶段流程id需要特殊保留下来，后面检索阶段版本需要
            Long stageProcessId = FastJsonHelper.getLongOrDefaultFromFormData(dataJsonObject, PaasStageEnum.STAGE_PROCESS_ID.getAttr(), BasicConstant.ZERO_LONG);
            if (!Objects.equals(stageProcessId, BasicConstant.ZERO_LONG)) {
                jsonObject.put(PaasStageEnum.STAGE_PROCESS_ID.getAttr(), dataJsonObject.get(PaasStageEnum.STAGE_PROCESS_ID.getAttr()));
            }
        }
    }

    /**
     * 精简一下pojo
     * @param saasNeedRedundantAttrPojo
     */
    private void setSaasNeedPojo(SaasNeedRedundantAttrPojo saasNeedRedundantAttrPojo) {
        if (Objects.nonNull(saasNeedRedundantAttrPojo.getNeedAddValidateDataDTO())) {
            saasNeedRedundantAttrPojo.getNeedAddValidateDataDTO().forEach(this::setBaseDTO2Null);
        }
        if (Objects.nonNull(saasNeedRedundantAttrPojo.getNeedDelValidateDataDTO())) {
            saasNeedRedundantAttrPojo.getNeedDelValidateDataDTO().forEach(this::setBaseDTO2Null);
        }
        if (Objects.nonNull(saasNeedRedundantAttrPojo.getNeedUpdateValidateDataDTO())) {
            saasNeedRedundantAttrPojo.getNeedUpdateValidateDataDTO().forEach(this::setBaseDTO2Null);
        }
        if (Objects.nonNull(saasNeedRedundantAttrPojo.getAddBatchDTO())) {
            setBaseDTO2Null(saasNeedRedundantAttrPojo.getAddBatchDTO());
        }
        if (Objects.nonNull(saasNeedRedundantAttrPojo.getAddPaymentSheetBatchDTO())) {
            setBaseDTO2Null(saasNeedRedundantAttrPojo.getAddPaymentSheetBatchDTO());
        }
        if (Objects.nonNull(saasNeedRedundantAttrPojo.getAddWriteOffPrepaymentBatchDTO())) {
            setBaseDTO2Null(saasNeedRedundantAttrPojo.getAddWriteOffPrepaymentBatchDTO());
        }
        if (Objects.nonNull(saasNeedRedundantAttrPojo.getBeforeSaveMyTeamDTO())) {
            setBaseDTO2Null(saasNeedRedundantAttrPojo.getBeforeSaveMyTeamDTO());
        }
        if (Objects.nonNull(saasNeedRedundantAttrPojo.getTeamAfterVerifyDTO())) {
            saasNeedRedundantAttrPojo.getTeamAfterVerifyDTO().setLoginUser(null);
        }
    }

    private void setBaseDTO2Null (BaseDTO baseDTO) {
        baseDTO.setBrowserInfo(null);
        baseDTO.setHttpHeader(null);
        baseDTO.setIp(null);
        baseDTO.setLocale(null);
        baseDTO.setLoginUser(null);
        baseDTO.setPlatform(null);
    }
}
